package code;
/**
 * 
 * @author
 * 
 * This class represents the item list. It is a double linked list and contains all the products' information.
 *
 */
public class ItemList {
	private ItemInfoNode head;
	private ItemInfoNode tail;

	public ItemList() {
		this.head = new ItemInfoNode();
		this.tail = new ItemInfoNode();
		this.head.setNext(this.tail);
		this.head.setPrev(null);
		this.tail.setPrev(this.head);
		this.tail.setNext(null);
	}
	
	/**
	 * 
	 * @param name  product name
	 * @param rfidTag rfidTagNumber
	 * @param price   product price
	 * @param initPosition initial position of product
	 * @throws PriceNegativeException  when price is negative
	 * @throws OriginalLocationLengthException when original location's length is not 6
	 * @throws OriginalLocationFormatException when original location's format is incorrect
	 * @throws RfidTagNumberLengthException when RfidTagNumber's length is not 9
	 * @throws RfidTagNumberNotHexException when RfidTagNumber is not in hex format
	 * 
	 * This method insert itemInfo into the itemList order by rfidTag 
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node in order to find the 
	 * correct position to insert to, so is's complexity is O(N)
	 */
	public void insertInfo(String name, String rfidTag, double price, String initPosition)
			throws PriceNegativeException, OriginalLocationLengthException, OriginalLocationFormatException,
			RfidTagNumberLengthException, RfidTagNumberNotHexException {
		ItemInfoNode itr = head;
		while (itr.getNext() != tail) {
			//when current item's next item's RfidTagNumber is larger than  rfidTag then we should insert 
			//the new node between current item and current item's next item
			if (itr != head && itr.getInfo().getRfidTagNumber().substring(1).compareTo(rfidTag) > 0) {
				break;
			}
			itr = itr.getNext();
		}
		//init the new node
		ItemInfoNode tnode = new ItemInfoNode();
		ItemInfo tinfo = new ItemInfo();
		tinfo.setPrice(price);
		tinfo.setOriginalLocation(initPosition);
		tinfo.setRfidTagNumber(rfidTag);
		tinfo.setProductName(name);
		tnode.setInfo(tinfo);
		//insert the new node
		tnode.setNext(itr.getNext());
		tnode.setPrev(itr);
		itr.getNext().setPrev(tnode);
		itr.setNext(tnode);
	}

	/**
	 * This method remove all items whose current location is 'out'
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node 
	 * in order to find the purchased product and remove them
	 * 
	 */
	public void removeAllPurchased() {
		System.out.println("The following item(s) have removed from the system:");
		printHeader();
		ItemInfoNode itr = head;
		while (itr.getNext() != tail) {
			if (itr.getNext().getInfo().getCurrentLocation().equalsIgnoreCase("out")) {
				printOneInfo(itr.getNext().getInfo());
				this.removeOneItem(itr.getNext());
			}
			itr = itr.getNext();
		}
	}

	/**
	 * 
	 * @param rfidTag   rfidTagNumber
	 * @param source    the source location
	 * @param dest      the destination location
	 * @return  true if item in source location's rfidTagNumber is equal to rfidTag ,false otherwise
	 * @throws LocationFormatException when source is euqal to out! or dest is in wrong location format
	 * 
	 * This method move the item from source location to dest location
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node to find the node in source location
	 */
	public boolean moveItem(String rfidTag, String source, String dest) throws LocationFormatException {
		Util.checkPostionFormat(dest);
		if (source.equalsIgnoreCase("out")) {
			throw new LocationFormatException("source is euqal to out!");
		}
		ItemInfoNode itr = head;
		boolean result = false;
		while (itr.getNext() != tail) {
			//find the item node
			if (itr.getNext().getInfo().getCurrentLocation().equalsIgnoreCase(source)) {
				//check if rfidTag is right
				if (itr.getNext().getInfo().getRfidTagNumber().equalsIgnoreCase(rfidTag)) {
					result = true;
					//if right then move
					itr.getNext().getInfo().setCurrentLocation(dest);
					break;
				}
			}
			itr = itr.getNext();
		}
		return result;
	}
	/**
	 * This method prints all the information of all iteminfonode in the itemlist
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node and print all the nodes' information one by one
	 */
	public void printAll() {
		printHeader();
		ItemInfoNode itr = head;
		while (itr.getNext() != tail) {
			ItemInfo info = itr.getNext().getInfo();
			//print one item info
			printOneInfo(info);
			itr = itr.getNext();
		}
	}
	/**
	 * 
	 * @param location  the location by which to print the product info
	 * 
	 * This method prints all the products' information which is placed in the location
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node in order to find all
	 * the products whose location is equal to the parameter
	 */
	public void printByLocation(String location) {
		printHeader();
		ItemInfoNode itr = head;
		while (itr.getNext() != tail) {
			ItemInfo info = itr.getNext().getInfo();
			//if location is equal then print
			if (info.getCurrentLocation().equalsIgnoreCase(location)) {
				printOneInfo(info);
			}
			itr = itr.getNext();
		}
	}
	
	/**
	 * This method move all the products which are not purchased or put in the cart back to their original locations
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node to find all the products which are not purchased 
	 * or put in the cart
	 */
	public void cleanStore() {
		System.out.println("The following item(s) have been moved back to their original locations:");
		printHeader();
		ItemInfoNode itr = head;
		while (itr.getNext() != tail) {
			ItemInfo info = itr.getNext().getInfo();
			//if not cart ot out and current location is not equal to original location then print it and reset the location
			if (!(info.getCurrentLocation().charAt(0) == 'c' || info.getCurrentLocation().charAt(0) == 'C'
					|| info.getCurrentLocation().equalsIgnoreCase("out"))
					&& !info.getCurrentLocation().equalsIgnoreCase(info.getOriginalLocation())) {
				printOneInfo(info);
				try {
					info.setCurrentLocation(info.getOriginalLocation());
				} catch (LocationFormatException e) {
				}
			}
			itr = itr.getNext();
		}
	}
	/**
	 * 
	 * @param cartNumber  the cart number to check out by
	 * @return  the total amount of the products' price which are in the cart with number cartNumber
	 * @throws LocationFormatException when cartNumber's format is incorrect
	 * 
	 * This method check out all the products in the cart with the number cartNumber
	 * 
	 * It's order of complexity using Big-O notation is O(N)
	 * Because it will iterate from the head node to the tail node to find all the products whose current
	 * positions are equal to cartNumber
	 */
	public double checkOut(String cartNumber) throws LocationFormatException{
		if (cartNumber.length() == 4) {
			if (!(cartNumber.charAt(0) == 'c' || cartNumber.charAt(0) == 'C')) {
				throw new LocationFormatException("Wrong cart number format!");
			}
			for (int i = 1; i < 4; i++) {
				char tc = cartNumber.charAt(i);
				if (!(tc >= '0' && tc <= '9')) {
					throw new LocationFormatException("Wrong cart number format!");
				}
			}
			printHeader();
			ItemInfoNode itr = head;
			double result = 0.0;
			while (itr.getNext() != tail) {
				ItemInfo info = itr.getNext().getInfo();
				if (info.getCurrentLocation().equalsIgnoreCase(cartNumber)) {
					printOneInfo(info);
					result += info.getPrice();
					info.setCurrentLocation("out");
				}
				itr = itr.getNext();
			}
			return result;
		}else{
			throw new LocationFormatException("Wrong cart number format!");
		}
	}
	
	/**
	 * 
	 * @param rfidTag the RFID number to print by
	 */
	public void printByRfidTag(String rfidTag) {
		printHeader();
		ItemInfoNode itr = head;
		while (itr.getNext() != tail) {
			ItemInfo info = itr.getNext().getInfo();
			if (info.getRfidTagNumber().equalsIgnoreCase(rfidTag)) {
				printOneInfo(info);
			}
			itr = itr.getNext();
		}
	}
	/**
	 * 
	 * @param itemInfoNode the item node to remove
	 */
	private void removeOneItem(ItemInfoNode itemInfoNode) {
		ItemInfoNode prev = itemInfoNode.getPrev();
		itemInfoNode.getNext().setPrev(prev);
		prev.setNext(itemInfoNode.getNext());
	}
	
	/**
	 * This method prints the header info
	 */
	private void printHeader() {
		System.out.println(String.format("%41s%14s", "Original", "Current"));
		System.out.println(
				String.format("%-18s%-15s%-15s%-15s%-7s", "Item Name", "RFID", "Location", "Location", "Price"));
		System.out.println(String.format("%-16s%-17s%-15s%-15s%-7s", "---------", "---------", "---------", "---------",
				"------"));
	}
	/**
	 * 
	 * @param info   the ItemInfo to print
	 * 
	 * This method prints one item info in one line in formatted form
	 */
	private void printOneInfo(ItemInfo info) {
		System.out.println(String.format("%-16s%-19s%-15s%-14s%4.2f", info.getProductName(), info.getRfidTagNumber(),
				info.getOriginalLocation(), info.getCurrentLocation(), info.getPrice()));
	}

}
